 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.console;

 import java.util.ArrayList ;
 import java.util.HashMap ;
 import java.util.List ;
 import java.util.Map ;

 import org.eclipse.core.runtime.ISafeRunnable;
 import org.eclipse.core.runtime.ListenerList;
 import org.eclipse.core.runtime.SafeRunner;
 import org.eclipse.jface.action.IToolBarManager;
 import org.eclipse.jface.action.Separator;
 import org.eclipse.jface.util.IPropertyChangeListener;
 import org.eclipse.jface.util.PropertyChangeEvent;
 import org.eclipse.jface.viewers.IBasicPropertyConstants;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.ui.IPartListener2;
 import org.eclipse.ui.IViewReference;
 import org.eclipse.ui.IViewSite;
 import org.eclipse.ui.IWorkbenchPart;
 import org.eclipse.ui.IWorkbenchPartReference;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.console.AbstractConsole;
 import org.eclipse.ui.console.ConsolePlugin;
 import org.eclipse.ui.console.IConsole;
 import org.eclipse.ui.console.IConsoleConstants;
 import org.eclipse.ui.console.IConsoleListener;
 import org.eclipse.ui.console.IConsoleManager;
 import org.eclipse.ui.console.IConsolePageParticipant;
 import org.eclipse.ui.console.IConsoleView;
 import org.eclipse.ui.part.IPage;
 import org.eclipse.ui.part.IPageBookViewPage;
 import org.eclipse.ui.part.MessagePage;
 import org.eclipse.ui.part.PageBook;
 import org.eclipse.ui.part.PageBookView;
 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;

 /**
  * Page book console view.
  *
  * @since 3.0
  */
 public class ConsoleView extends PageBookView implements IConsoleView, IConsoleListener, IPropertyChangeListener, IPartListener2 {
     
     /**
      * Whether this console is pinned.
      */
     private boolean fPinned = false;
     
     /**
      * Stack of consoles in MRU order
      */
     private List fStack = new ArrayList ();
     
     /**
      * The console being displayed, or <code>null</code> if none
      */
     private IConsole fActiveConsole = null;
     
     /**
      * Map of consoles to dummy console parts (used to close pages)
      */
     private Map fConsoleToPart;
     
     /**
      * Map of consoles to array of page participants
      */
     private Map fConsoleToPageParticipants;
     
     /**
      * Map of parts to consoles
      */
     private Map fPartToConsole;
     
     /**
      * Whether this view is active
      */
     private boolean fActive = false;
     
     // actions
 private PinConsoleAction fPinAction = null;
     private ConsoleDropDownAction fDisplayConsoleAction = null;
     
     private OpenConsoleAction fOpenConsoleAction = null;

     private boolean fScrollLock;

     private boolean isAvailable() {
         return getPageBook() != null && !getPageBook().isDisposed();
     }

     /* (non-Javadoc)
      * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
      */
     public void propertyChange(PropertyChangeEvent event) {
         Object source = event.getSource();
         if (source instanceof IConsole && event.getProperty().equals(IBasicPropertyConstants.P_TEXT)) {
             if (source.equals(getConsole())) {
                 updateTitle();
             }
         }

     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.IPartListener#partClosed(org.eclipse.ui.IWorkbenchPart)
      */
     public void partClosed(IWorkbenchPart part) {
         super.partClosed(part);
         fPinAction.update();
     }

     /* (non-Javadoc)
      * @see org.eclipse.debug.internal.ui.console.IConsoleView#getConsole()
      */
     public IConsole getConsole() {
         return fActiveConsole;
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.part.PageBookView#showPageRec(org.eclipse.ui.part.PageBookView.PageRec)
      */
     protected void showPageRec(PageRec pageRec) {
         // don't show the page when pinned, unless this is the first console to be added
 // or its the default page
 if (fActiveConsole != null && pageRec.page != getDefaultPage() && fPinned && fConsoleToPart.size() > 1) {
             IConsole console = (IConsole)fPartToConsole.get(pageRec.part);
             if (!fStack.contains(console)) {
                 fStack.add(console);
             }
             return;
         }
         
         IConsole recConsole = (IConsole)fPartToConsole.get(pageRec.part);
         if (recConsole!=null && recConsole.equals(fActiveConsole)) {
             return;
         }
         
         super.showPageRec(pageRec);
         fActiveConsole = recConsole;
         IConsole tos = null;
         if (!fStack.isEmpty()) {
             tos = (IConsole) fStack.get(0);
         }
         if (tos != null && !tos.equals(fActiveConsole)) {
             deactivateParticipants(tos);
         }
         if (fActiveConsole != null && !fActiveConsole.equals(tos)) {
             fStack.remove(fActiveConsole);
             fStack.add(0,fActiveConsole);
             activateParticipants(fActiveConsole);
         }
         updateTitle();
         updateHelp();
         // update console actions
 if (fPinAction != null) {
             fPinAction.update();
         }
         IPage page = getCurrentPage();
         if (page instanceof IOConsolePage) {
             ((IOConsolePage)page).setAutoScroll(!fScrollLock);
         }
     }
     
     /**
      * Activates the participants for the given console, if any.
      *
      * @param console
      */
     private void activateParticipants(IConsole console) {
         // activate
 if (console != null && fActive) {
             final ListenerList listeners = getParticipants(console);
             if (listeners != null) {
                 Object [] participants = listeners.getListeners();
                 for (int i = 0; i < participants.length; i++) {
                     final IConsolePageParticipant participant = (IConsolePageParticipant) participants[i];
                     SafeRunner.run(new ISafeRunnable() {
                         public void run() throws Exception {
                             participant.activated();
                         }
                         public void handleException(Throwable exception) {
                             ConsolePlugin.log(exception);
                             listeners.remove(participant);
                         }
                     });
                 }
             }
         }
     }

     /**
      * Returns a stack of consoles in the view in MRU order.
      *
      * @return a stack of consoles in the view in MRU order
      */
     protected List getConsoleStack() {
         return fStack;
     }

     /**
      * Updates the view title based on the active console
      */
     protected void updateTitle() {
         IConsole console = getConsole();
         if (console == null) {
             setContentDescription(ConsoleMessages.ConsoleView_0);
         } else {
             String newName = console.getName();
             String oldName = getContentDescription();
             if (newName!=null && !(newName.equals(oldName))) {
                 setContentDescription(console.getName());
             }
         }
     }
     
     protected void updateHelp() {
         IConsole console = getConsole();
         String helpContextId = null;
         if (console instanceof AbstractConsole) {
             AbstractConsole abs = (AbstractConsole) console;
             helpContextId = abs.getHelpContextId();
         }
         if (helpContextId == null) {
             helpContextId = IConsoleHelpContextIds.CONSOLE_VIEW;
         }
         PlatformUI.getWorkbench().getHelpSystem().setHelp(getPageBook().getParent(), helpContextId);
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.part.PageBookView#doDestroyPage(org.eclipse.ui.IWorkbenchPart, org.eclipse.ui.part.PageBookView.PageRec)
      */
     protected void doDestroyPage(IWorkbenchPart part, PageRec pageRecord) {
         IConsole console = (IConsole)fPartToConsole.get(part);
         
         // dispose page participants
 ListenerList listeners = (ListenerList) fConsoleToPageParticipants.remove(console);
         if (listeners != null) {
             Object [] participants = listeners.getListeners();
             for (int i = 0; i < participants.length; i++) {
                 final IConsolePageParticipant participant = (IConsolePageParticipant) participants[i];
                 SafeRunner.run(new ISafeRunnable() {
                     public void run() throws Exception {
                         participant.dispose();
                     }
                     public void handleException(Throwable exception) {
                         ConsolePlugin.log(exception);
                     }
                 });
             }
         }

         IPage page = pageRecord.page;
         page.dispose();
         pageRecord.dispose();
         console.removePropertyChangeListener(this);
                         
         // empty cross-reference cache
 fPartToConsole.remove(part);
         fConsoleToPart.remove(console);
         if (fPartToConsole.isEmpty()) {
             fActiveConsole = null;
         }
         
         // update console actions
 fPinAction.update();
     }
     
     /**
      * Returns the page participants registered for the given console, or <code>null</code>
      *
      * @param console
      * @return registered page participants or <code>null</code>
      */
     private ListenerList getParticipants(IConsole console) {
         return (ListenerList) fConsoleToPageParticipants.get(console);
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.part.PageBookView#doCreatePage(org.eclipse.ui.IWorkbenchPart)
      */
     protected PageRec doCreatePage(IWorkbenchPart dummyPart) {
         ConsoleWorkbenchPart part = (ConsoleWorkbenchPart)dummyPart;
         final IConsole console = part.getConsole();
         final IPageBookViewPage page = console.createPage(this);
         initPage(page);
         page.createControl(getPageBook());
         console.addPropertyChangeListener(this);
         
         // initialize page participants
 IConsolePageParticipant[] consoleParticipants = ((ConsoleManager)getConsoleManager()).getPageParticipants(console);
         final ListenerList participants = new ListenerList();
         for (int i = 0; i < consoleParticipants.length; i++) {
             participants.add(consoleParticipants[i]);
         }
         fConsoleToPageParticipants.put(console, participants);
         Object [] listeners = participants.getListeners();
         for (int i = 0; i < listeners.length; i++) {
             final IConsolePageParticipant participant = (IConsolePageParticipant) listeners[i];
             SafeRunner.run(new ISafeRunnable() {
                 public void run() throws Exception {
                     participant.init(page, console);
                 }
                 public void handleException(Throwable exception) {
                     ConsolePlugin.log(exception);
                     participants.remove(participant);
                 }
             });
         }
         
         PageRec rec = new PageRec(dummyPart, page);
         return rec;
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.part.PageBookView#isImportant(org.eclipse.ui.IWorkbenchPart)
      */
     protected boolean isImportant(IWorkbenchPart part) {
         return part instanceof ConsoleWorkbenchPart;
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.IWorkbenchPart#dispose()
      */
     public void dispose() {
         super.dispose();
         getViewSite().getPage().removePartListener((IPartListener2)this);
         ConsoleManager consoleManager = (ConsoleManager) ConsolePlugin.getDefault().getConsoleManager();
         consoleManager.removeConsoleListener(this);
         consoleManager.unregisterConsoleView(this);
     }

     /**
      * Returns the console manager.
      *
      * @return the console manager
      */
     private IConsoleManager getConsoleManager() {
         return ConsolePlugin.getDefault().getConsoleManager();
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.part.PageBookView#createDefaultPage(org.eclipse.ui.part.PageBook)
      */
     protected IPage createDefaultPage(PageBook book) {
         MessagePage page = new MessagePage();
         page.createControl(getPageBook());
         initPage(page);
         return page;
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.console.IConsoleListener#consolesAdded(org.eclipse.ui.console.IConsole[])
      */
     public void consolesAdded(final IConsole[] consoles) {
         if (isAvailable()) {
             Runnable r = new Runnable () {
                 public void run() {
                     for (int i = 0; i < consoles.length; i++) {
                         if (isAvailable()) {
                             IConsole console = consoles[i];
                             // ensure it's still registered since this is done asynchronously
 IConsole[] allConsoles = getConsoleManager().getConsoles();
                             for (int j = 0; j < allConsoles.length; j++) {
                                 IConsole registered = allConsoles[j];
                                 if (registered.equals(console)) {
                                     ConsoleWorkbenchPart part = new ConsoleWorkbenchPart(console, getSite());
                                     fConsoleToPart.put(console, part);
                                     fPartToConsole.put(part, console);
                                     partActivated(part);
                                     break;
                                 }
                             }

                         }
                     }
                 }
             };
             asyncExec(r);
         }
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.console.IConsoleListener#consolesRemoved(org.eclipse.ui.console.IConsole[])
      */
     public void consolesRemoved(final IConsole[] consoles) {
         if (isAvailable()) {
             Runnable r = new Runnable () {
                 public void run() {
                     for (int i = 0; i < consoles.length; i++) {
                         if (isAvailable()) {
                             IConsole console = consoles[i];
                             fStack.remove(console);
                             ConsoleWorkbenchPart part = (ConsoleWorkbenchPart)fConsoleToPart.get(console);
                             if (part != null) {
                                 partClosed(part);
                             }
                             if (getConsole() == null) {
                                 IConsole[] available = getConsoleManager().getConsoles();
                                 if (available.length > 0) {
                                     display(available[available.length - 1]);
                                 }
                             }
                         }
                     }
                 }
             };
             asyncExec(r);
         }
     }

     /**
      * Constructs a console view
      */
     public ConsoleView() {
         super();
         fConsoleToPart = new HashMap ();
         fPartToConsole = new HashMap ();
         fConsoleToPageParticipants = new HashMap ();
         
         ConsoleManager consoleManager = (ConsoleManager) ConsolePlugin.getDefault().getConsoleManager();
         consoleManager.registerConsoleView(this);
     }
     
     protected void createActions() {
         fPinAction = new PinConsoleAction(this);
         fDisplayConsoleAction = new ConsoleDropDownAction(this);
         ConsoleFactoryExtension[] extensions = ((ConsoleManager)ConsolePlugin.getDefault().getConsoleManager()).getConsoleFactoryExtensions();
         if (extensions.length > 0) {
             fOpenConsoleAction = new OpenConsoleAction();
         }
     }

     protected void configureToolBar(IToolBarManager mgr) {
         mgr.add(new Separator(IConsoleConstants.LAUNCH_GROUP));
         mgr.add(new Separator(IConsoleConstants.OUTPUT_GROUP));
         mgr.add(new Separator("fixedGroup")); //$NON-NLS-1$
 mgr.add(fPinAction);
         mgr.add(fDisplayConsoleAction);
         if (fOpenConsoleAction != null) {
             mgr.add(fOpenConsoleAction);
         }
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.console.IConsoleView#display(org.eclipse.ui.console.IConsole)
      */
     public void display(IConsole console) {
         if (fPinned && fActiveConsole != null) {
             return;
         }
         if (console.equals(fActiveConsole)) {
             return;
         }
         ConsoleWorkbenchPart part = (ConsoleWorkbenchPart)fConsoleToPart.get(console);
         if (part != null) {
             partActivated(part);
         }
     }

     /*/* (non-Javadoc)
      * @see org.eclipse.ui.console.IConsoleView#pin(org.eclipse.ui.console.IConsole)
      */
     public void setPinned(boolean pin) {
         fPinned = pin;
         if (fPinAction != null) {
             fPinAction.update();
         }
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.console.IConsoleView#isPinned()
      */
     public boolean isPinned() {
         return fPinned;
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.part.PageBookView#getBootstrapPart()
      */
     protected IWorkbenchPart getBootstrapPart() {
         return null;
     }
     
     /**
      * Registers the given runnable with the display
      * associated with this view's control, if any.
      *
      * @see org.eclipse.swt.widgets.Display#asyncExec(java.lang.Runnable)
      */
     public void asyncExec(Runnable r) {
         if (isAvailable()) {
             getPageBook().getDisplay().asyncExec(r);
         }
     }
     
     /**
      * Creates this view's underlying viewer and actions.
      * Hooks a pop-up menu to the underlying viewer's control,
      * as well as a key listener. When the delete key is pressed,
      * the <code>REMOVE_ACTION</code> is invoked. Hooks help to
      * this view. Subclasses must implement the following methods
      * which are called in the following order when a view is
      * created:<ul>
      * <li><code>createViewer(Composite)</code> - the context
      * menu is hooked to the viewer's control.</li>
      * <li><code>createActions()</code></li>
      * <li><code>configureToolBar(IToolBarManager)</code></li>
      * <li><code>getHelpContextId()</code></li>
      * </ul>
      * @see IWorkbenchPart#createPartControl(Composite)
      */
     public void createPartControl(Composite parent) {
         super.createPartControl(parent);
         createActions();
         IToolBarManager tbm= getViewSite().getActionBars().getToolBarManager();
         configureToolBar(tbm);
         updateForExistingConsoles();
         getViewSite().getActionBars().updateActionBars();
         PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, IConsoleHelpContextIds.CONSOLE_VIEW);
         getViewSite().getPage().addPartListener((IPartListener2)this);
     }
     
     /**
      * Initialize for existing consoles
      */
     private void updateForExistingConsoles() {
         IConsoleManager manager = getConsoleManager();
         // create pages for consoles
 IConsole[] consoles = manager.getConsoles();
         consolesAdded(consoles);
         // add as a listener
 manager.addConsoleListener(this);
     }
     
     /* (non-Javadoc)
      * @see org.eclipse.ui.console.IConsoleView#warnOfContentChange(org.eclipse.ui.console.IConsole)
      */
     public void warnOfContentChange(IConsole console) {
         IWorkbenchPart part = (IWorkbenchPart)fConsoleToPart.get(console);
         if (part != null) {
             IWorkbenchSiteProgressService service = (IWorkbenchSiteProgressService) part.getSite().getAdapter(IWorkbenchSiteProgressService.class);
             if (service != null) {
                 service.warnOfContentChange();
             }
         }
     }
     
     public Object getAdapter(Class key) {
         Object adpater = super.getAdapter(key);
         if (adpater == null) {
             IConsole console = getConsole();
             if (console != null) {
                 ListenerList listeners = getParticipants(console);
                 // an adapter can be asked for before the console participants are created
 if (listeners != null) {
                     Object [] participants = listeners.getListeners();
                     for (int i = 0; i < participants.length; i++) {
                         IConsolePageParticipant participant = (IConsolePageParticipant) participants[i];
                         adpater = participant.getAdapter(key);
                         if (adpater != null) {
                             return adpater;
                         }
                     }
                 }
             }
         }
         return adpater;
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.IPartListener2#partActivated(org.eclipse.ui.IWorkbenchPartReference)
      */
     public void partActivated(IWorkbenchPartReference partRef) {
         if (isThisPart(partRef)) {
             fActive = true;
             activateParticipants(fActiveConsole);
         }
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.IPartListener2#partBroughtToTop(org.eclipse.ui.IWorkbenchPartReference)
      */
     public void partBroughtToTop(IWorkbenchPartReference partRef) {
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui.IWorkbenchPartReference)
      */
     public void partClosed(IWorkbenchPartReference partRef) {
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.IPartListener2#partDeactivated(org.eclipse.ui.IWorkbenchPartReference)
      */
     public void partDeactivated(IWorkbenchPartReference partRef) {
         if (isThisPart(partRef)) {
             fActive = false;
             deactivateParticipants(fActiveConsole);
         }
     }
     
     protected boolean isThisPart(IWorkbenchPartReference partRef) {
         if (partRef instanceof IViewReference) {
             IViewReference viewRef = (IViewReference) partRef;
             if (viewRef.getId().equals(getViewSite().getId())) {
                 String secId = viewRef.getSecondaryId();
                 String mySec = null;
                 if (getSite() instanceof IViewSite) {
                     mySec = ((IViewSite)getSite()).getSecondaryId();
                 }
                 if (mySec == null) {
                     return secId == null;
                 }
                 return mySec.equals(secId);
             }
         }
         return false;
     }

     /**
      * Deactivates participants for the given console, if any.
      *
      * @param console console to deactivate
      */
     private void deactivateParticipants(IConsole console) {
         // deactivate
 if (console != null) {
             final ListenerList listeners = getParticipants(console);
             if (listeners != null) {
                 Object [] participants = listeners.getListeners();
                 for (int i = 0; i < participants.length; i++) {
                     final IConsolePageParticipant participant = (IConsolePageParticipant) participants[i];
                     SafeRunner.run(new ISafeRunnable() {
                         public void run() throws Exception {
                             participant.deactivated();
                         }
                         public void handleException(Throwable exception) {
                             ConsolePlugin.log(exception);
                             listeners.remove(participant);
                         }
                     });
                 }
             }
         }
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.IPartListener2#partOpened(org.eclipse.ui.IWorkbenchPartReference)
      */
     public void partOpened(IWorkbenchPartReference partRef) {
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.IPartListener2#partHidden(org.eclipse.ui.IWorkbenchPartReference)
      */
     public void partHidden(IWorkbenchPartReference partRef) {
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.IPartListener2#partVisible(org.eclipse.ui.IWorkbenchPartReference)
      */
     public void partVisible(IWorkbenchPartReference partRef) {
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.IPartListener2#partInputChanged(org.eclipse.ui.IWorkbenchPartReference)
      */
     public void partInputChanged(IWorkbenchPartReference partRef) {
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.console.IConsoleView#setScrollLock(boolean)
      */
     public void setScrollLock(boolean scrollLock) {
         fScrollLock = scrollLock;

         IPage page = getCurrentPage();
         if (page instanceof IOConsolePage) {
             ((IOConsolePage)page).setAutoScroll(!scrollLock);
         }
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.console.IConsoleView#getScrollLock()
      */
     public boolean getScrollLock() {
         return fScrollLock;
     }
     
     /* (non-Javadoc)
      * @see org.eclipse.ui.console.IConsoleView#pin(org.eclipse.ui.console.IConsole)
      */
     public void pin(IConsole console) {
         if (console == null) {
             setPinned(false);
         } else {
             if (isPinned()) {
                 setPinned(false);
             }
             display(console);
             setPinned(true);
         }
     }
 }

